﻿using System;
using System.Collections.Generic;
using System.Linq;
using BlizzardApi;
using BlizzardApi.Model;
using Model;
using Service.Framework;
using Utility;
using Achievement = Model.Achievement;
using Faction = BlizzardApi.Model.Faction;
using Guild = BlizzardApi.Model.Guild;

namespace Service
{
    class Program
    {
        static void Main()
        {
        	Locator.Resolver = new StructureMapResolver();

        	var service = Locator.Resolve<IWowService>();
            var repo = Locator.Resolve<IRepository>();

            /*
            var races = service.GetRaces();
            foreach (var race in races)
                Console.WriteLine("Race: {0} {1} {2}", race.ID, race.Name, race.Side);

            var classes = service.GetClasses();
            foreach (var @class in classes)
                Console.WriteLine("Class: {0} {1}", @class.ID, @class.Name);

            var rewards = service.GetGuildRewards();
            foreach (var reward in rewards)
                Console.WriteLine("Reward: {0} {1} {2} {3}", reward.Achievement != null ? reward.Achievement.ID : -1, reward.Achievement != null ?reward.Achievement.Title : "NONE", reward.Item.ID, reward.Item.Name);

            var perks = service.GetGuildPerks();
            foreach (var perk in perks)
                Console.WriteLine("Perk: {0} {1} {2}", perk.GuildLevel, perk.Spell.Name, perk.Spell.SubText);

            var itemClasses = service.GetItemClasses();
            foreach (var itemClass in itemClasses)
                Console.WriteLine("Item Class: {0} {1}", itemClass.Class, itemClass.Name);

            var item = service.GetItem(38268);
            Console.WriteLine("Item: {0} {1}", item.ID, item.Name);
            if (!string.IsNullOrEmpty(item.Icon))
                service.GetIcon(IconSize.Large, item.Icon).Save(item.ID + ".jpg", ImageFormat.Jpeg);
            */

            SyncRealms(service, repo);
            SyncGuildAchievements(service, repo);
            SyncCharacterAchievements(service, repo);

            var realm = repo.Query<Realm>().FirstOrDefault(x => x.Name == "Darkspear");
            
        	var auctionFiles = service.GetAuctionFiles("darkspear");
            foreach (var auctionFile in auctionFiles)
            {
                var auctionHouses = service.GetAuctionHouses(auctionFile);
                if (auctionHouses != null)
                {
                    ConsoleHelper.WriteLine(ConsoleColor.DarkBlue, ConsoleColor.White, "Alliance Auctions: {0}",
                                            auctionHouses.Alliance.Auctions.Count());
                    ConsoleHelper.WriteLine(ConsoleColor.DarkRed, ConsoleColor.White, "Horde Auctions: {0}",
                                            auctionHouses.Horde.Auctions.Count());
                    ConsoleHelper.WriteLine(ConsoleColor.DarkYellow, ConsoleColor.White, "Neutral Auctions: {0}",
                                            auctionHouses.Neutral.Auctions.Count());

                    var guilds = auctionHouses.Alliance.Auctions
                        .Concat(auctionHouses.Horde.Auctions)
                        .Concat(auctionHouses.Neutral.Auctions)
                        .Select(x => x.Owner)
                        .Distinct()
                        .Select(a => service.GetCharacter("darkspear", a))
                        .Where(c => c != null && c.Guild != null)
                        .Select(x => x.Guild.Name)
                        .Distinct()
                        .Select(x => service.GetGuild("darkspear", x))
                        .Where(x => x != null)
                        .ToList();

                    foreach (var guild in guilds)
                    {
                        DumpGuild(guild);
                        using (var tx = repo.BeginTransaction())
                        {
                            try
                            {
                                StoreGuild(guild, realm, repo);
                                tx.Commit();
                                ConsoleHelper.WriteLine(ConsoleColor.White, ConsoleColor.Black, "Saved");
                            }
                            catch
                            {
                                tx.Rollback();
                                throw;
                            }
                        }
                    }
                }
            }

            Console.WriteLine("Done");
            Console.ReadKey(true);
        }

        private static void SyncCharacterAchievements(IWowService service, IRepository repo)
        {
            var characterAchievements = service.GetCharacterAchievementCategories()
                .SelectMany(c => c.Categories != null ? c.Categories.Concat(new[] { c }) : new[] { c })
                .SelectMany(x => x.Achievements)
                .ToList();
            foreach (var characterAchievement in characterAchievements)
            {
                var theAchievement = characterAchievement;
                var achievement = repo.Query<Achievement>().FirstOrDefault(x => x.ExternalKey == theAchievement.ID);
                if (achievement == null)
                {
                    achievement = new Achievement
                                      {
                                          Description = theAchievement.Description,
                                          Points = theAchievement.Points,
                                          Title = theAchievement.Title,
                                          ExternalKey = theAchievement.ID,
                                          Faction = Model.Faction.Neutral,
                                      };
                    repo.Save(achievement);
                }
            }
        }

        private static void SyncGuildAchievements(IWowService service, IRepository repo)
        {
            var guildAchievements = service.GetGuildAchievementCategories()
                .SelectMany(c => c.Categories != null ? c.Categories.Concat(new[] { c }) : new[] { c })
                .SelectMany(x => x.Achievements)
                .ToList();
            foreach (var characterAchievement in guildAchievements)
            {
                var theAchievement = characterAchievement;
                var achievement = repo.Query<Achievement>().FirstOrDefault(x => x.ExternalKey == theAchievement.ID);
                if (achievement == null)
                {
                    achievement = new Achievement
                                      {
                                          Description = theAchievement.Description,
                                          Points = theAchievement.Points,
                                          Title = theAchievement.Title,
                                          ExternalKey = theAchievement.ID,
                                          Faction = Model.Faction.Neutral,
                                      };
                    repo.Save(achievement);
                }
            }
        }

        private static void SyncRealms(IWowService service, IRepository repo)
        {
            var realmStatuses = service.GetRealmStatuses();
            foreach (var realmStatus in realmStatuses)
            {
                var theRealm = realmStatus;
                var realm = repo.Query<Realm>().FirstOrDefault(x => x.Name == theRealm.Name);
                if (realm == null)
                {
                    realm = new Realm
                                {
                                    Name = theRealm.Name,
                                };
                    repo.Save(realm);
                }
            }            
        }

        private static void DumpGuild(Guild guild)
        {
            Console.Write("Guild: {0} {1} ", guild.Name, guild.Level);
            ConsoleHelper.WriteLine(guild.Side == Faction.Alliance ? ConsoleColor.Blue : ConsoleColor.Red, ConsoleColor.White, guild.Side.ToString());
            Console.WriteLine("\tGuild Master: {0}", guild.Members.OrderBy(x => x.Rank).Select(x => x.Character.Name).First());
            Console.WriteLine("\tGuild Members: {0}", guild.Members.Count());
            Console.WriteLine("\tGuild Achievements: {0}", guild.Achievements.AchievementsCompleted.Count());
            Console.WriteLine("\tGuild Points: {0}", guild.AchievementPoints);
        }

        private static void StoreGuild(Guild guild, Realm realm, IRepository repository)
        {
            var newGuild = new Model.Guild
                               {
                                   Faction = guild.Side == Faction.Alliance ? Model.Faction.Alliance : Model.Faction.Horde,
                                   Level = guild.Level,
                                   Name = guild.Name,
                                   Realm = realm,
                               };
            repository.Save(newGuild);
            StoreGuildAchievements(guild, newGuild, repository);
            StoreGuildAchievementCriteria(guild, newGuild, repository);
            foreach (var guildMember in guild.Members)
                StoreGuildMember(guildMember, newGuild, realm, repository);
        }

        private static IDictionary<int, Achievement> _achievementCache;
        private static IDictionary<int, Achievement> AchievementCache(IRepository repository)
        {
            return _achievementCache ?? (_achievementCache = repository.Query<Achievement>().ToList().ToDictionary(x => x.ExternalKey));
        }

        private static void StoreGuildAchievements(Guild guild, Model.Guild newGuild, IRepository repository)
        {
            if (guild.Achievements != null && guild.Achievements.AchievementsCompleted != null && guild.Achievements.AchievementsCompletedTimestamp != null)
            {
                var achievements = guild.Achievements.AchievementsCompleted.ToArray();
                var achievementCompleted = guild.Achievements.AchievementsCompletedTimestamp.ToArray();
                for (int i=0;i<achievements.Length;i++)
                {
                    var externalKey = achievements[i];
                    var completed = achievementCompleted[i];

                    Achievement achievement;
                    AchievementCache(repository).TryGetValue(externalKey, out achievement);
                    if (achievement != null)
                    {
                        var progress = FindAchievementProgress(repository, newGuild, achievement);
                        if (progress == null)
                        {
                            progress = new AchievementProgress
                                           {
                                               Guild = newGuild,
                                               Achievement = achievement,
                                               Completed = completed,
                                           };
                            repository.Save(progress);
                            SaveAchievementProgress(repository, newGuild, achievement, progress);
                        }
                        else if (progress.Completed != completed)
                        {
                            progress.Completed = completed;
                            repository.Update(progress);
                        }
                    }
                    else
                    {
                        ConsoleHelper.WriteLine(ConsoleColor.Red, ConsoleColor.White, "Missing Achievement: {0}", externalKey);
                    }
                }
            }
        }

        private static IDictionary<int, AchievementCriteria> _achievementCriteriaCache;
        private static IDictionary<int, AchievementCriteria> AchievementCriteriaCache(IRepository repository)
        {
            return _achievementCriteriaCache ?? (_achievementCriteriaCache = repository.Query<AchievementCriteria>().ToList().ToDictionary(x => x.ExternalKey));
        }

        private static IDictionary<KeyValuePair<int, int>, AchievementProgress> _achievementProgress;
        private static AchievementProgress FindAchievementProgress(IRepository repository, Model.Guild guild, Achievement achievement)
        {
            if (_achievementProgress == null)
                _achievementProgress = repository.Query<AchievementProgress>().ToList().ToDictionary(x => new KeyValuePair<int, int>(x.Guild.ID, x.Achievement.ID));
            
            AchievementProgress result;
            _achievementProgress.TryGetValue(new KeyValuePair<int, int>(guild.ID, achievement.ID), out result);
            return result;
        }

        private static void SaveAchievementProgress(IRepository repository, Model.Guild guild, Achievement achievement, AchievementProgress progress)
        {
            var x = FindAchievementProgress(repository, guild, achievement);
            if (x != null)
                throw new Exception("Acievement Progress already exists");
            _achievementProgress.Add(new KeyValuePair<int, int>(guild.ID, achievement.ID), progress);
        }


        private static void StoreGuildAchievementCriteria(Guild guild, Model.Guild newGuild, IRepository repository)
        {
            if (guild.Achievements != null && guild.Achievements.Criteria != null && guild.Achievements.CriteriaQuantity != null && guild.Achievements.CriteriaTimestamp != null)
            {
                var criteria = guild.Achievements.Criteria.ToArray();
                var criteriaCreated = guild.Achievements.CriteriaCreated.ToArray();
                var criteriaQuantities = guild.Achievements.CriteriaQuantity.ToArray();
                var criteriaTimestamps = guild.Achievements.CriteriaTimestamp.ToArray();

                for (int i = 0; i < criteria.Length; i++)
                {
                    var externalKey = criteria[i];
                    var created = criteriaCreated[i];
                    var quantity = criteriaQuantities[i];
                    var timestamp = criteriaTimestamps[i];

                    AchievementCriteria achievementCriteria;
                    AchievementCriteriaCache(repository).TryGetValue(externalKey, out achievementCriteria);
                    if (achievementCriteria == null)
                        continue;
                    var achievementProgress = FindAchievementProgress(repository, newGuild, achievementCriteria.Achievement);
                    if (achievementProgress == null)
                    {
                        achievementProgress = new AchievementProgress
                                                  {
                                                      Guild = newGuild,
                                                      Achievement = achievementCriteria.Achievement,
                                                  };
                        repository.Save(achievementProgress);
                        SaveAchievementProgress(repository, newGuild, achievementCriteria.Achievement, achievementProgress);
                    }
                    var criteriaProgress = new AchievementCriteriaProgress
                                               {
                                                   Completed = timestamp,
                                                   Created = created,
                                                   Quantity = quantity,
                                                   AchievementProgress = achievementProgress,
                                                   Criteria = achievementCriteria
                                               };
                    repository.Save(criteriaProgress);
                    achievementProgress.CriteriaProgress.Add(criteriaProgress);
                    repository.Update(achievementProgress);
                }
            }
        }

        private static void StoreGuildMember(GuildMembership guildMember, Model.Guild guild, Realm realm, IRepository repository)
        {
            var toon = new Model.Character
                           {
                               Guild = guild,
                               Realm = realm,
                               Gender = Gender.Male,
                               Class = Enum<Model.Class>.FromInt(guildMember.Character.Class),
                               Level = guildMember.Character.Level,
                               Race = Enum<Model.Race>.FromInt(guildMember.Character.Race),
                               Name = guildMember.Character.Name
                           };
            repository.Save(toon);
        }
    }
}
